{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CasADi demo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## What is CasADi?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " * A tool for quick & efficient implementation of algorithms for dynamic optimization\n",
    " * Open source, LGPL-licensed,    <a href=\"http://casadi.org\">casadi.org</a>\n",
    " * C++ / C++11\n",
    " * Interfaces to Python, Haskell, (Matlab?)\n",
    " * Numerical backends: <a href=\"https://projects.coin-or.org/Ipopt\">IPOPT</a>, <a href=\"http://computation.llnl.gov/casc/sundials/main.html\">Sundials</a>, ...\n",
    " * Developers in group of Moritz Diehl:\n",
    "   * Joel Andersson\n",
    "   * Joris Gillis\n",
    "   * Greg Horn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Outline of demo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " * Scalar expression (SX) graphs\n",
    " * Functions of SX graphs\n",
    " * Matrices of scalar expressions\n",
    " * Automatic differentiation (AD)\n",
    " * Integrators\n",
    " * Matrix expression (MX) graphs\n",
    " * Functions of MX graphs\n",
    " * Solving an optimal control problem"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Scalar expression (SX) graphs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import print_function\n",
    "from pylab import *\n",
    "from casadi import *\n",
    "from casadi.tools import *  # for dotdraw\n",
    "from IPython.display import Image, display\n",
    "%matplotlib inline\n",
    "\n",
    "def view_dot(graph):\n",
    "   plt = Image(graph.create_png())\n",
    "   display(plt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = SX.sym(\"x\")  # scalar symbolic primitives\n",
    "y = SX.sym(\"y\")\n",
    "\n",
    "z = x*sin(x+y)   # common mathematical operators"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "view_dot(dotgraph(z,direction=\"BT\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "J = jacobian(z,x)\n",
    "print(J)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "view_dot(dotgraph(J,direction=\"BT\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Note 1: subexpressions are shared.\n",
    ">\n",
    ">   Graph $\\leftrightarrow$ Tree\n",
    ">   \n",
    ">  Different from Maple, Matlab symbolic, sympy, ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A (very) little bit of Computer Algebra"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(x*y/x-y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "H = hessian(z,x)\n",
    "print(H)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Functions of SX graphs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sort graph into algorithm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = Function(\"f\",[x,y],[z])\n",
    "\n",
    "f.disp(True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Note 2: re-use of tape variables: live-variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f(1.2,3.4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f(1.2,x+y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.generate(\"f\")\n",
    "print(open(\"f.c\").read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matrices of scalar expressions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A = SX.sym(\"A\",3,3)\n",
    "B = SX.sym(\"B\",3)\n",
    "print(A)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(solve(A,B))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(trace(A))   # Trace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(mtimes(A,B))   # Matrix multiplication\n",
    "#print(A @ B)        # Matrix multiplication in Python3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(norm_fro(A))  # Frobenius norm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(A[2,:])     # Slicing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Rule 1: Everything is a matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(A.shape, z.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "I = SX.eye(3)\n",
    "print(I)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Ak = kron(I,A)\n",
    "print(Ak)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Rule 1: Everything is a sparse matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Ak.sparsity().spy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A.sparsity().spy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.sparsity().spy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Automatic differentiation (AD)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Consider an ode:\n",
    "\n",
    "\\begin{equation}\n",
    "\\dot{p} = (1 - q^2)p-q+u\n",
    "\\end{equation}\n",
    "\\begin{equation}\n",
    "\\dot{q} = p\n",
    "\\end{equation}\n",
    "\\begin{equation}\n",
    "\\dot{c} = p^2+q^2+u^2\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t = SX.sym(\"t\")    # time\n",
    "u = SX.sym(\"u\")    # control\n",
    "p = SX.sym(\"p\")\n",
    "q = SX.sym(\"q\")\n",
    "c = SX.sym(\"c\")\n",
    "x = vertcat(p,q,c) # state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ode = vertcat((1 - q**2)*p - q + u, p, p**2+q**2+u**2)\n",
    "print(ode, ode.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "J = jacobian(ode,x)\n",
    "print(J)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = Function(\"f\",[t,u,x],[ode])\n",
    "\n",
    "ffwd = f.forward(1)\n",
    "\n",
    "fadj = f.reverse(1)\n",
    "\n",
    "# side-by-side printing\n",
    "print('{:*^24} || {:*^28} || {:*^28}'.format(\"f\",\"ffwd\",\"fadj\"))\n",
    "def short(f):\n",
    "    import re\n",
    "    return re.sub(r\", a\\.k\\.a\\. \\\"(\\w+)\\\"\",r\". \\1\",f.str(True).replace(\", No description available\",\"\").replace(\"Input \",\"\").replace(\"Output \",\"\"))\n",
    "for l in zip(short(f).split(\"\\n\"),short(ffwd).split(\"\\n\"),short(fadj).split(\"\\n\")):\n",
    "  print('{:<24} || {:<28} || {:<28}'.format(*l))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Performing **forward sweeps** gives the **columns** of J"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(I)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(3):\n",
    "  print(ffwd(t,u,x, ode, 0,0,I[:,i]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(J)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Performing **adjoint sweeps** gives the **rows** of J"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(3):\n",
    "  print(fadj(t,u,x, ode, I[:,i])[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Often, you can do better than slicing with unit vectors\n",
    "\n",
    "> Note 3: CasADi does graph coloring for efficient sparse jacobians"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Integrators"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$\\dot{x}=f(x,u,t)$ with $x = [p,q,c]^T$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = {'x':x,'t':t,'p':u,'ode':ode}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Construct an integrating block $x_{k+1} = \\Phi(f;\\Delta t;x_k,u_k)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tf = 10.0\n",
    "N = 20\n",
    "dt = tf/N"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Phi = integrator(\"Phi\",\"cvodes\",f,{\"tf\":dt})\n",
    "\n",
    "x0 = DM([0,1,0])\n",
    "\n",
    "print(Phi(x0=x0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = x0\n",
    "xs = [x]\n",
    "\n",
    "for i in range(N):\n",
    "  x = Phi(x0=x)[\"xf\"]\n",
    "  \n",
    "  xs.append(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot(horzcat(*xs).T)\n",
    "legend([\"p\",\"q\",\"c\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Rule 2: Everything is a Function  (see http://docs.casadi.org)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matrix expression (MX) graphs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Note 4: this is what makes CasADi stand out among AD tools"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Recall"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 3\n",
    "\n",
    "A = SX.sym(\"A\",n,n)\n",
    "B = SX.sym(\"B\",n,n)\n",
    "C = mtimes(A,B)\n",
    "print(C)\n",
    "view_dot(dotgraph(C,direction='BT'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What if you **don't want** to expand into scalar operations?  ( avoid $O(n^3)$ storage)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A = MX.sym(\"A\",n,n)\n",
    "B = MX.sym(\"B\",n,n)\n",
    "C = mtimes(A,B)\n",
    "print(C)\n",
    "view_dot(dotgraph(C,direction='BT'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What if you **cannot** expand into matrix operations?  ( numerical algorithm )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "C = solve(A,B)\n",
    "print(C)\n",
    "view_dot(dotgraph(C,direction='BT'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X0 = MX.sym(\"x\",3)\n",
    "\n",
    "XF = Phi(x0=X0)[\"xf\"]\n",
    "print(XF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = sin(XF)+X0\n",
    "view_dot(dotgraph(expr,direction='BT'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Functions of MX graphs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "F = Function(\"F\",[X0],[  expr  ])\n",
    "print(F)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(F(x0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "J = Function(\"J\",[X0],[  jacobian(expr,X0)  ])\n",
    "\n",
    "print(J(x0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This shows how an integrator-call can be embedded in matrix graph.\n",
    "\n",
    "More possibilities:  external compiled library, a call to Matlab/Scipy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Solving an optimal control problem"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\\begin{equation}\n",
    "\\begin{array}{cl}\n",
    "\\underset{p(.),q(.),u(.)}{\\text{minimize}}  & \\displaystyle \\int_{0}^{T}{ p(t)^2 + q(t)^2 + u(t)^2 dt} \\\\\\\\\n",
    "\\text{subject to}\n",
    "& \\dot{p} = (1 - q^2)p-q+u   \\\\\\\\\n",
    "& \\dot{q} = p \\\\\\\\\n",
    "& p(0) = 0, q(0) = 1 \\\\\\\\\n",
    "&-1 \\le u(t) \\le 1\n",
    "\\end{array}\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "Remember, $\\dot{x}=f(x,u,t)$ with $x = [p,q,c]^T$\n",
    "\n",
    "\\begin{equation}\n",
    "\\begin{array}{cl}\n",
    "\\underset{x(.),u(.)}{\\text{minimize}}  & c(T) \\\\\\\\\n",
    "\\text{subject to}\n",
    "& \\dot{x} = f(x,u) \\\\\\\\\n",
    "& p(0) = 0, q(0) = 1, c(0)= 0 \\\\\\\\\n",
    "&-1 \\le u(t) \\le 1\n",
    "\\end{array}\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Discretization with multiple shooting\n",
    "\n",
    "\\begin{equation}\n",
    "\\begin{array}{cl}\n",
    "\\underset{x_{\\bullet},u_{\\bullet}}{\\text{minimize}}  & c_N \\\\\\\\\n",
    "\\text{subject to}\n",
    "& x_{k+1} - \\Phi(x_k,u_k) = 0 , \\quad \\quad k = 0,1,\\ldots, (N-1) \\\\\\\\\n",
    "& p_0 = 0, q_0 = 1, c_0 = 0 \\\\\\\\\n",
    "&-1 \\le u_k \\le 1  , \\quad \\quad k = 0,1,\\ldots, (N-1) \n",
    "\\end{array}\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cast as NLP\n",
    "\n",
    "\\begin{equation}\n",
    "\\begin{array}{cl}\n",
    "\\underset{X}{\\text{minimize}}  & F(X,P) \\\\\\\\\n",
    "\\text{subject to}\n",
    "& \\text{lbx} \\le X \\le \\text{ubx} \\\\\\\\\n",
    "& \\text{lbg} \\le G(X,P) \\le \\text{ubg} \\\\\\\\\n",
    "\\end{array}\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = struct_symMX([\n",
    "     (\n",
    "      entry(\"x\", repeat=N+1, struct=struct([\"p\",\"q\",\"c\"]) ),\n",
    "      entry(\"u\", repeat=N)\n",
    "     )\n",
    "    ])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "X is a symbolic matrix primitive, but with fancier indexing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(X.shape)\n",
    "print((N+1)*3+N)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Demo: $\\Phi(x_0,u_0)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Xf = Phi( x0=X[\"x\",0],p=X[\"u\",0] )[\"xf\"]\n",
    "\n",
    "print(Xf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$ x_{k+1} - \\Phi(x_k,u_k) = 0 , \\quad \\quad k = 0,1,\\ldots, (N-1)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = [] # List of constraint expressions\n",
    "\n",
    "for k in range(N):\n",
    "  Xf = Phi( x0=X[\"x\",k],p=X[\"u\",k] )[\"xf\"]\n",
    "  g.append( X[\"x\",k+1]-Xf )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "obj = X[\"x\",N,\"c\"] # c_N\n",
    "\n",
    "nlp = {\"x\":X, \"g\": vcat(g), \"f\": obj}\n",
    "\n",
    "print(nlp)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Block structure in the constraint Jacobian"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "jacG = jacobian(nlp[\"g\"],nlp[\"x\"])\n",
    "\n",
    "S = jacG.sparsity()\n",
    "\n",
    "print(S.shape)\n",
    "\n",
    "DM.ones(S)[:20,:20].sparsity().spy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Recall\n",
    "\n",
    "\\begin{equation}\n",
    "\\begin{array}{cl}\n",
    "\\underset{X}{\\text{minimize}}  & F(X,P) \\\\\\\\\n",
    "\\text{subject to}\n",
    "& \\text{lbx} \\le X \\le \\text{ubx} \\\\\\\\\n",
    "& \\text{lbg} \\le G(X,P) \\le \\text{ubg} \\\\\\\\\n",
    "\\end{array}\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "solver = nlpsol(\"solver\",\"ipopt\",nlp)\n",
    "\n",
    "lbx = X(-inf)\n",
    "ubx = X(inf)\n",
    "\n",
    "lbx[\"u\",:] = -1; ubx[\"u\",:] = 1   #   -1 <= u(t) <= 1\n",
    "\n",
    "lbx[\"x\",0] = ubx[\"x\",0] = x0      # Initial condition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sol_out = solver(\n",
    "           lbg = 0, # Equality constraints for shooting constraints\n",
    "           ubg = 0, #    0 <= g <= 0\n",
    "           lbx = lbx,\n",
    "           ubx = ubx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(sol_out[\"x\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sol = X(sol_out[\"x\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot(horzcat(*sol[\"x\",:]).T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "step(range(N),sol[\"u\",:])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Wrapping up"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Showcase: kite-power optimization by Greg Horn, using CasADi backend"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import YouTubeVideo\n",
    "YouTubeVideo('tmjIBpb43j0')\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "YouTubeVideo('SW6ZJzcMWAk')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Distinction with other software:\n",
    "<table>\n",
    "    <tr>\n",
    "        <th>ACADOtoolkit</th><th>CasADi</th>\n",
    "    </tr>\n",
    "    <tr>\n",
    "        <td><ul><li>Black-box solver</li><li>Standard-form OCP</li><li>Good at small-scale real-time NMPC</li><li>Easy to get started</li></ul></td>\n",
    "        <td><ul><li>Write your own solver using a pool of building-blocks</li><li>No limitations on formulation</li><li>Good at large-scale OCP</li><li>Easy to extend</li></ul></td>\n",
    "    </tr>\n",
    "</table>\n",
    "\n",
    "<table>\n",
    "    <tr>\n",
    "        <th>Other operator-overloading AD tools</th><th>CasADi</th>\n",
    "    </tr>\n",
    "    <tr>\n",
    "        <td><ul><li>Scalar graphs only, checkpointing</li></ul></td>\n",
    "        <td><ul><li>Scalar and matrix graphs</li><li>Batteries included: Ipopt, Sundials</li></ul></td>\n",
    "    </tr>\n",
    "</table>\n",
    "\n",
    "Closest similarity: AMPL"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
